using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Input.Touch;
using Microsoft.Xna.Framework.Media;
using FloatingScore;
using System.Xml.Serialization;
using System.IO;


namespace WindowsGame1
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
        public class Game1 : Microsoft.Xna.Framework.Game
        {
            private GraphicsDeviceManager graphics;
            private SpriteBatch spriteBatch;
            private Nave nave;
            private NaveCompi naveCompi;
            private Fondo fondo;
            private int frameCounter = 0;
            private List<Enemigo1> enemigos;
            private int over = 0;
            private SpriteFontFloatScore f;
            private Enemigo1 enemigoABorrar = null;
            private bool colision2 = false;
            private Menu mimenu;
            private int timeMenu = 0;
            private int timeScores = 0;
            private bool multiplayer = false;
            private bool score = false;
            private bool menu = true;
            bool menuUp, menuDown = false; // variables usadas en el control de las teclas
            public readonly string HighScoresFilename = "highscores.lst";
           // private GifAnimationCs animation;
           

            public Game1()
            {
                graphics = new GraphicsDeviceManager(this);
                enemigos = new List<Enemigo1>();
                Content.RootDirectory = "Content";            
            }

            /// <summary>
            /// Allows the game to perform any initialization it needs to before starting to run.
            /// This is where it can query for any required services and load any non-graphic
            /// related content.  Calling base.Initialize will enumerate through any components
            /// and initialize them as well.
            /// </summary>
            protected override void Initialize()
            {
                //Console.WriteLine(graphics.PreferredBackBufferHeight + "x" + graphics.PreferredBackBufferWidth);
                fondo = new Fondo(graphics.PreferredBackBufferHeight, graphics.PreferredBackBufferWidth);
                nave = new Nave(graphics.PreferredBackBufferHeight, graphics.PreferredBackBufferWidth,multiplayer);
                naveCompi = new NaveCompi(graphics.PreferredBackBufferHeight, graphics.PreferredBackBufferWidth);

                this.Window.Title="Space Burst";
                f = new SpriteFontFloatScore(graphics);

                //graphics.IsFullScreen = true;
                initializeScores();
                base.Initialize();            
            }

            /// <summary>
            /// LoadContent will be called once per game and is the place to load
            /// all of your content.
            /// </summary>
            protected override void LoadContent()
            {
                // Create a new SpriteBatch, which can be used to draw textures.
                spriteBatch = new SpriteBatch(GraphicsDevice);
                fondo.LoadContent(Content);
                nave.LoadContent(Content);
                naveCompi.LoadContent(Content);
                mimenu = new Menu(Color.White, Color.Red, Content.Load<SpriteFont>("sp"), Content.Load<Texture2D>("Check"));
                mimenu.addItemMenu("Un jugador", new Vector2(100, 120));
                mimenu.addItemMenu("Multijugador", new Vector2(100, 220));
                mimenu.addItemMenu("Puntuaciones", new Vector2(100, 320));
                mimenu.addItemMenu("Salir", new Vector2(100, 420));
                f.Score = "0";
                //this.animation = this.Content.Load<GifAnimationCs>("exploison");
            }

            /// <summary>
            /// UnloadContent will be called once per game and is the place to unload
            /// all content.
            /// </summary>
            protected override void UnloadContent()
            {
                // TODO: Unload any non ContentManager content here
            }

            /// <summary>
            /// Allows the game to run logic such as updating the world,
            /// checking for collisions, gathering input, and playing audio.
            /// </summary>
            /// <param name="gameTime">Provides a snapshot of timing values.</param>
            protected override void Update(GameTime gameTime)
            {
                //graphics.ApplyChanges();
                if (menu)
                {
                    if (timeMenu > 60)
                    {
                        updateMenu(gameTime);
                    }
                    else
                    {
                        timeMenu++;

                    }
                }
                else if (score)
                {
                    if (timeScores > 60)
                    {
                        updateScores();
                    }
                    else
                    {
                        timeScores++;
                    }
                }
                else
                {

                    if (over < 3)
                    {
                        // Cuidado con esto, no usamos mando.
                        if (Keyboard.GetState().IsKeyDown(Keys.Escape))
                            this.Exit();
                        fondo.Update();
                        if (multiplayer)
                            naveCompi.Update();
                        nave.Update();
                        UpdateEnemigos();
                        UpdateColisiones();
                        UpdateColisionNave(nave);
                        if (multiplayer)
                        {
                            UpdateColisionNaveCompi(naveCompi);
                        }
                        // animation.Update(gameTime.ElapsedGameTime.Ticks);
                        base.Update(gameTime);
                    }
                    else if (Keyboard.GetState().IsKeyDown(Keys.Enter))
                    {
                        enemigos=new List<Enemigo1>();
                        nave.reiniciarPosition(graphics.PreferredBackBufferHeight, graphics.PreferredBackBufferWidth,multiplayer);
                        naveCompi.reiniciarPosition(graphics.PreferredBackBufferHeight, graphics.PreferredBackBufferWidth);
                        fondo.reiniciarPosition(graphics.PreferredBackBufferHeight, graphics.PreferredBackBufferWidth);
                        SaveHighScore(int.Parse(f.Score), 1,"Player 1");
                        over = 0;
                        f.Score = "0";
                        timeMenu = 0;
                        menu = true;
                        multiplayer = false;
                    }
                    else if (Keyboard.GetState().IsKeyDown(Keys.Escape))
                    {
                        this.Exit();
                    }
                }
            }
            private void updateMenu(GameTime gameTime)
            {

                mimenu.Update(gameTime);

                KeyboardState k = Keyboard.GetState();

                if (k.IsKeyDown(Keys.Escape))
                {
                    this.Exit();
                }
                if (k.IsKeyDown(Keys.Up) && menuUp)
                {
                    mimenu.seleccionarAnterior();
                    menuUp = false;
                }
                else if (k.IsKeyUp(Keys.Up))
                {
                    menuUp = true;
                }
                if (k.IsKeyDown(Keys.Down) && menuDown)
                {
                    mimenu.seleccionarSiguiente();
                    menuDown = false;
                }
                else if (k.IsKeyUp(Keys.Down))
                {
                    menuDown = true;
                }
                if (k.IsKeyDown(Keys.Enter))
                {
                    switch (mimenu.ItemActual)
                    {
                        case 0:
                            menu=false;
                            break;
                        case 1:
                            multiplayer = true;
                            nave.reiniciarPosition(graphics.PreferredBackBufferHeight, graphics.PreferredBackBufferWidth, multiplayer);
                            menu = false;
                            break;
                        case 2:
                            menu = false;
                            score = true;
                            timeScores = 0;
                            break;
                        case 3:
                            this.Exit();
                            break;
                    }
                }

            }
            private void updateScores()
            {
                KeyboardState k = Keyboard.GetState();

                    if (k.IsKeyDown(Keys.Escape))
                    {
                        this.Exit();
                    }
                    else if (k.IsKeyDown(Keys.Enter))
                    {
                        menu = true;
                        score = false;
                        timeMenu = 0;
                    }
                
            }
            private void UpdateEnemigos()
            {
                frameCounter++;
                if (frameCounter > 60)
                {
                    Random r = new Random();
                    Enemigo1 e = new Enemigo1(
                        new Vector2(r.Next(graphics.PreferredBackBufferWidth), -57),
                        graphics.PreferredBackBufferHeight,
                        graphics.PreferredBackBufferWidth,
                        Content
                        );
                    enemigos.Add(e);
                    e.FueraDePantalla += new EventHandler(FueraDePantallaHandler);
                    frameCounter = 0;
                }
                enemigos.ForEach(x => x.Update());  
            }
            private void UpdateColisiones()
            {
                //eliminamos cualquier enemigo que haya colisionado contra cualquier disparo en la pantalla
                bool colision = false;
                //Enemigo1 enemigoABorrar = null;
                Disparo disparoABorrar = null;

                foreach (Enemigo1 e in enemigos)
                {
                    if (e.colisionDisparo == 0)
                    {
                        foreach (Disparo d in nave.Disparos)
                        {
                            if (ColisionEnemigoDisparo(e, d))
                            {
                                //si hay colision ponemos el tag a true, almacenamos el disparo y el enemigo y rompemos el primer bucle
                                colision = true;
                                enemigoABorrar = e;
                                disparoABorrar = d;
                                break;
                            }
                        }

                        foreach (Disparo d in naveCompi.Disparos)
                        {
                            if (ColisionEnemigoDisparo(e, d))
                            {
                                //si hay colision ponemos el tag a true, almacenamos el disparo y el enemigo y rompemos el primer bucle
                                colision = true;
                                enemigoABorrar = e;
                                disparoABorrar = d;
                                break;
                            }
                        }

                        //si el tag esta activo rompemos el segundo bucle
                        if (colision)
                            break;
                    }
                }
                //si el tag esta activo borramos el enemigo y el disparo
                if (colision)
                {
                    explosion();
                    enemigoABorrar.colisionDisparo = 1;
                    //enemigos.Remove(enemigoABorrar);
                    nave.Disparos.Remove(disparoABorrar);
                    naveCompi.Disparos.Remove(disparoABorrar);
                    f.Score = (Int32.Parse(f.Score) + 10).ToString();
                }
            }

            private void explosion()
            {
                colision2 = true;
            }

            

            private void UpdateColisionNave(Nave d)
            {
                //eliminamos cualquier enemigo que haya colisionado contra cualquier disparo en la pantalla
                bool colisionNave = false;
                Enemigo1 enemigoABorrar = null;
                //Disparo disparoABorrar = null;

                foreach (Enemigo1 e in enemigos)
                {
                    if (e.colisionDisparo == 0)
                    {
                        if (ColisionEnemigoNave(e, d))
                        {
                            //si hay colision ponemos el tag a true, almacenamos el disparo y el enemigo y rompemos el primer bucle
                            colisionNave = true;
                            enemigoABorrar = e;
                            break;
                        }                       
                    }
                    
                 }
                //si el tag esta activo borramos el enemigo y el disparo
                if (colisionNave)
                {
                    //enemigos.Remove(enemigoABorrar);
                    enemigoABorrar.colisionDisparo = 1;
                    ++over;
                }
            }
            private void UpdateColisionNaveCompi(NaveCompi d)
            {
                //eliminamos cualquier enemigo que haya colisionado contra cualquier disparo en la pantalla
                bool colisionNave = false;
                Enemigo1 enemigoABorrar = null;
                //Disparo disparoABorrar = null;

                foreach (Enemigo1 e in enemigos)
                {
                    if (e.colisionDisparo == 0)
                    {
                        if (ColisionEnemigoNaveCompi(e, d))
                        {
                            //si hay colision ponemos el tag a true, almacenamos el disparo y el enemigo y rompemos el primer bucle
                            colisionNave = true;
                            enemigoABorrar = e;
                            break;
                        }
                       
                    }

                }
                //si el tag esta activo borramos el enemigo y el disparo
                if (colisionNave)
                {
                    //enemigos.Remove(enemigoABorrar);
                    enemigoABorrar.colisionDisparo = 1;
                    ++over;
                }
            }
            
            private void FueraDePantallaHandler(Object sender, EventArgs args)
            {
                enemigos.Remove((Enemigo1)sender);
            }
            /// <summary>
            /// This is called when the game should draw itself.
            /// </summary>
            /// <param name="gameTime">Provides a snapshot of timing values.</param>
            protected override void Draw(GameTime gameTime)
            {
                
                GraphicsDevice.Clear(Color.Black);
                
                if (menu)
                {
                    mimenu.Draw(spriteBatch);
                }
                else if (score)
                {
                    spriteBatch.Begin();

                    graphics.GraphicsDevice.Clear(Color.Black);

                    String scoreString = "Player Name   Score   Level";
                    
                    int yScore=10;


                    spriteBatch.DrawString(Content.Load<SpriteFont>("score"), scoreString, new Vector2(10, yScore), Color.White);

                    HighScoreData data = LoadHighScores(HighScoresFilename);

                    for (int i = 0; i < data.Count; i++)
                    {
                        scoreString = "   "+data.PlayerName[i]+"   "+data.Score[i]+"   "+data.Level[i];
                        spriteBatch.DrawString(Content.Load<SpriteFont>("score"), scoreString, new Vector2(10, yScore+=20), Color.White);
                    }

                    base.Draw(gameTime);
                    spriteBatch.End();
                }
                else
                {
                    spriteBatch.Begin();
                    if (over < 3)
                    {

                        fondo.Draw(spriteBatch);

                        for (int i = 0; i < enemigos.Count; i++)
                        {
                            Enemigo1 e = enemigos.ElementAt(i);
                            if (e.colisionDisparo != 2)
                            {
                                e.Draw(spriteBatch);
                            }
                            else
                            {
                                enemigos.Remove(e);
                                i--;
                            }
                        }
                        nave.Draw(spriteBatch);

                        if (multiplayer)
                            naveCompi.Draw(spriteBatch);

                        Texture2D cor = null;

                        if (over == 0)
                            cor = Content.Load<Texture2D>("corazon3");
                        else if (over == 1)
                            cor = Content.Load<Texture2D>("corazon2");
                        else
                            cor = Content.Load<Texture2D>("corazon1");

                        spriteBatch.Draw(cor, new Vector2(0, 0), new Rectangle(0, 0, cor.Width, cor.Height), Color.White);

                        f.Alive = true;
                        f.SpriteFont = Content.Load<SpriteFont>("spritefont");


                        f.Draw(spriteBatch);

                        base.Draw(gameTime);
                    }
                    else
                    {
                        spriteBatch.Draw(Content.Load<Texture2D>("gameOver"), new Vector2(0, 0), new Rectangle(0, 0, graphics.PreferredBackBufferWidth, graphics.PreferredBackBufferHeight), Color.Aquamarine);

                    }
                    if (colision2)
                    {
                        Texture2D texture = Content.Load<Texture2D>("explosion");
                        spriteBatch.Draw(texture, enemigoABorrar.Posicion, new Rectangle(0, 0, 44, 31), Color.White);
                        spriteBatch.Draw(texture, enemigoABorrar.Posicion, new Rectangle(44, 31, 44, 31), Color.White);
                        spriteBatch.Draw(texture, enemigoABorrar.Posicion, new Rectangle(88, 62, 44, 31), Color.White);
                        spriteBatch.Draw(texture, enemigoABorrar.Posicion, new Rectangle(176, 124, 44, 31), Color.White);
                        colision2 = false;
                    }

                    if (!colision2)
                        spriteBatch.End();
                }

            }
            private bool ColisionEnemigoDisparo(Enemigo1 e, Disparo d)
            {
                //Si los rectangle de e y de d se intersectan comprobamos la colision.
                if(e.Bounds.Intersects(d.Bounds))
                    return ColisionPixel(d.Imagen, e.Imagen, d.Posicion, e.Posicion);
                return false;
            }

            private bool ColisionEnemigoNave(Enemigo1 e, Nave d)
            {
                //Si los rectangle de e y de d se intersectan comprobamos la colision.
                if (e.Bounds.Intersects(d.Bounds))
                    return ColisionPixel(d.Imagen, e.Imagen, d.Posicion, e.Posicion);
                return false;
            }

            private bool ColisionEnemigoNaveCompi(Enemigo1 e, NaveCompi d)
            {
                //Si los rectangle de e y de d se intersectan comprobamos la colision.
                if (e.Bounds.Intersects(d.Bounds))
                    return ColisionPixel(d.Imagen, e.Imagen, d.Posicion, e.Posicion);
                return false;
            }
            public static bool ColisionPixel(Texture2D texturaA, Texture2D texturaB, Vector2 posicionA, Vector2 posicionB)
            {
                bool colisionPxAPx = false;

                uint[] bitsA = new uint[texturaA.Width * texturaA.Height];
                uint[] bitsB = new uint[texturaB.Width * texturaB.Height];

                Rectangle rectanguloA = new Rectangle(Convert.ToInt32(posicionA.X), Convert.ToInt32(posicionA.Y), texturaA.Width, texturaA.Height);
                Rectangle rectanguloB = new Rectangle(Convert.ToInt32(posicionB.X), Convert.ToInt32(posicionB.Y), texturaB.Width, texturaB.Height);
                //almacenamos los datos de los pixeles en las variables locales bitsA y bitsB
                texturaA.GetData<uint>(bitsA);
                texturaB.GetData<uint>(bitsB);
                
                //almacenamos las coordenadas que delimitaran la zona en la que trabajaremos
                int x1 = Math.Max(rectanguloA.X, rectanguloB.X);
                int x2 = Math.Min(rectanguloA.X + rectanguloA.Width, rectanguloB.X + rectanguloB.Width);

                int y1 = Math.Max(rectanguloA.Y, rectanguloB.Y);
                int y2 = Math.Min(rectanguloA.Y + rectanguloA.Height, rectanguloB.Y + rectanguloB.Height);

                for (int y = y1; y < y2; ++y)
                {
                    for (int x = x1; x < x2; ++x)
                    {
                        if (((bitsA[(x - rectanguloA.X) + (y - rectanguloA.Y) * rectanguloA.Width] & 0xFF000000) >> 24) > 20 &&
                            ((bitsB[(x - rectanguloB.X) + (y - rectanguloB.Y) * rectanguloB.Width] & 0xFF000000) >> 24) > 20)
                        {
                            //Se comprueba el canal alpha de las dos imagenes en el mismo pixel. Si los dos son visibles hay colision.
                            colisionPxAPx = true;
                            break;
                        }
                    }

                    // Rompe el bucle si la condicion ya se ha cumplido.
                    if (colisionPxAPx)
                    {
                        break;
                    }
                }

                return colisionPxAPx;
            }
            Boolean clic_Item(Rectangle rect, int x, int y)
            {
                return (x >= rect.X &&
                     x <= rect.X + rect.Width &&
                     y >= rect.Y &&
                     y <= rect.Y + rect.Height);
            }

            private void SaveHighScore(int score, int currentLevel,String playerName)
            {
                // Create the data to save
                HighScoreData data = LoadHighScores(HighScoresFilename);

                int scoreIndex = -1;
                for (int i = 0; i < data.Count; i++)
                {
                    if (score > data.Score[i])
                    {
                        scoreIndex = i;
                        break;
                    }
                }

                if (scoreIndex > -1)
                {
                    //New high score found ... do swaps
                    for (int i = data.Count - 1; i > scoreIndex; i--)
                    {
                        data.PlayerName[i] = data.PlayerName[i - 1];
                        data.Score[i] = data.Score[i - 1];
                        data.Level[i] = data.Level[i - 1];
                    }

                    data.PlayerName[scoreIndex] = playerName; //Retrieve User Name Here
                    data.Score[scoreIndex] = score;
                    data.Level[scoreIndex] = currentLevel + 1;

                    SaveHighScores(data, HighScoresFilename);
                }
            }

            protected void initializeScores()
            {
                // Get the path of the save game
                string fullpath = Path.Combine(HighScoresFilename);

                // Check to see if the save exists
                if (!File.Exists(fullpath))
                {
                    //If the file doesn't exist, make a fake one...
                    // Create the data to save
                    HighScoreData data = new HighScoreData(5);
                    data.PlayerName[0] = "Neil";
                    data.Level[0] = 10;
                    data.Score[0] = 50000;

                    data.PlayerName[1] = "Shawn";
                    data.Level[1] = 10;
                    data.Score[1] = 10000;

                    data.PlayerName[2] = "Mark";
                    data.Level[2] = 9;
                    data.Score[2] = 1000;

                    data.PlayerName[3] = "Cindy";
                    data.Level[3] = 7;
                    data.Score[3] = 100;

                    data.PlayerName[4] = "Sam";
                    data.Level[4] = 1;
                    data.Score[4] = 10;

                    SaveHighScores(data, HighScoresFilename);
                }

                //base.Initialize();
            }

            [Serializable]
            public struct HighScoreData
            {
                public string[] PlayerName;
                public int[] Score;
                public int[] Level;

                public int Count;

                public HighScoreData(int count)
                {
                    PlayerName = new string[count];
                    Score = new int[count];
                    Level = new int[count];

                    Count = count;
                }
            }

            public static void SaveHighScores(HighScoreData data, string filename)
            {
                // Get the path of the save game
                string fullpath = Path.Combine(filename);

                // Open the file, creating it if necessary
                FileStream stream = File.Open(fullpath, FileMode.OpenOrCreate);
                try
                {
                    // Convert the object to XML data and put it in the stream
                    XmlSerializer serializer = new XmlSerializer(typeof(HighScoreData));
                    serializer.Serialize(stream, data);
                }
                finally
                {
                    // Close the file
                    stream.Close();
                }
            }

            public static HighScoreData LoadHighScores(string filename)
            {
                HighScoreData data;

                // Get the path of the save game
                string fullpath = Path.Combine(/*StorageContainer.TitleLocation,*/ filename);

                // Open the file
                FileStream stream = File.Open(fullpath, FileMode.OpenOrCreate,
                FileAccess.Read);
                try
                {

                    // Read the data from the file
                    XmlSerializer serializer = new XmlSerializer(typeof(HighScoreData));
                    data = (HighScoreData)serializer.Deserialize(stream);
                }
                finally
                {
                    // Close the file
                    stream.Close();
                }

                return (data);
            }

        }



}
